/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_zml.htm
 *
 * Zhiqim Zml is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.zml.expression.operator;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.zhiqim.kernel.Global;
import org.zhiqim.kernel.model.maps.HashMapSO;
import org.zhiqim.kernel.model.maps.MapSO;
import org.zhiqim.kernel.util.Classes;
import org.zhiqim.kernel.util.Types;
import org.zhiqim.zml.Expression;
import org.zhiqim.zml.ExpressionParser;
import org.zhiqim.zml.ZmlEngine;
import org.zhiqim.zml.ZmlVariable;
import org.zhiqim.zml.exception.ExpressionException;
import org.zhiqim.zml.exception.StatementException;
import org.zhiqim.zml.expression.Operator;
import org.zhiqim.zml.expression.primitive._Variable;
import org.zhiqim.zml.statement._Function;

/**
 * 方法操作符，括号function(param1, param2)或obj.method(param1, param2)
 * 
 * @see _Bracket 括号操作符，和方法操作符不同的是内部是表达式，方法操作符括号内为参数
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class _Method extends Operator
{
    private String methodName;
    
    private Expression obj;
    private ArrayList<Expression> paramList;
    
    public _Method(String methodName)
    {
        this.methodName = methodName;
        this.paramList = new ArrayList<Expression>();
    }
    
    @Override
    public int getType()
    {
        return METHOD;
    }
    
    @Override
    public Object build(ZmlVariable variableMap) throws ExpressionException
    {
        if (obj == null)
        {//如果前置对象不存在，则认为是调用全局函数
            return ZmlVariable.chkDynamicVar(buildFunction(variableMap));
        }
        else if (obj instanceof _New)
        {//如果前置对象是_New表示调用构造函数
            return ZmlVariable.chkDynamicVar(buildNewInstance(variableMap));
        }
        else
        {//对象存在则认为是调用对象的方法
            return ZmlVariable.chkDynamicVar(buildMethod(variableMap));
        }
    }
    
    /** 方法是全局函数 */
    private Object buildFunction(ZmlVariable variableMap) throws ExpressionException
    {
        //先查本身是否有函数和是否包含里有函数
        _Function _function = null;
        try{_function = variableMap.getFunction(methodName);}catch (StatementException e){throw new ExpressionException("方法表达式{"+this+"}"+e.getMessage(), e);}
        
        if (_function == null)
        {//当前ZML中没有查全局
            ZmlEngine engine = variableMap.getEngine();
            if (engine != null)
            {            
                try{_function = engine.getFunction(methodName);}catch(Exception e){throw new ExpressionException("方法表达式{"+this+"}"+e.getMessage(), e);}
            }
        }
        
        if (_function == null)
        {//全局也没有抛异常
            throw new ExpressionException("方法表达式{"+this+"}ZML内部和全局函数都未找到该函数的定义");
        }
        
        //设置函数变量表，1.原变量表，2.实参变量传入
        MapSO pageMap = new HashMapSO();
        pageMap.putAll(variableMap.getVariableMap());
        
        //采用javascript的方式，按实参顺序对应形参顺序
        List<String> varList = _function.getVariableNameList();
        for (int i=0;i<paramList.size();i++)
        {
            String key = varList.get(i);
            Object value = paramList.get(i).build(variableMap);
            pageMap.put(key, value);
        }
        
        try
        {//转到全局函数处理，新建变量表，重新定义页变量表
            ZmlVariable variable = new ZmlVariable();
            variable.setZml(variableMap.getZml());
            variable.setVariableMap(pageMap);
            variable.setContextMap(variableMap.getContextMap());
            variable.setFunctionMap(variableMap.getFunctionMap());
            
            return _function.build(variable);
        }
        catch (StatementException e)
        {
            throw new ExpressionException("方法表达式{"+this+"}对应的全局函数处理异常", e);
        }
    }
    
    /** 方法是构造函数 */
    private Object buildNewInstance(ZmlVariable variableMap) throws ExpressionException
    {
        Class<?> newClass = ZmlEngine.getJavaClass(methodName);
        if (newClass == null)
            newClass = Global.forName(methodName);
        
        if (newClass == null)
            throw new ExpressionException("方法表达式{"+this+"}对应的new构造函数类名不正确");
        
        Object[] paramArray = new Object[paramList.size()];
        for (int i=0;i<paramList.size();i++)
        {
            paramArray[i] = paramList.get(i).build(variableMap);
        }
        
        //找到一个构造函数匹配上的
        Constructor<?> constructor = null;
        Constructor<?>[] constructors = newClass.getConstructors();
        for (Constructor<?> c : constructors)
        {
          //判断参数个数
            Class<?>[] paramTypes = c.getParameterTypes();
            if (paramTypes.length != paramArray.length)
                continue;
            
            //判断每个参数类型
            boolean isSuccess = true;
            for (int i=0;i<paramTypes.length;i++)
            {
                Class<?> clazz = paramTypes[i];
                Object param = paramArray[i];
                if (param == null)
                {//参数为null时，要求不是基本类型
                    if (Types.isPrimitive(clazz))
                    {
                        isSuccess = false;
                        break;
                    }
                }
                else if (Types.isPrimitive(clazz))
                {//基本类型，数值型不一定匹配，要适配
                    if ((Types.isInteger(clazz) && !Types.isInteger(param))//long,int,short,byte
                        || (Types.isDecimal(clazz) && !Types.isDecimal(param))//float,double
                        || (Types.isBoolean(clazz) && !Types.isBoolean(param))//boolean
                        || (Types.isChar(clazz) && !Types.isChar(param)))//char
                    {
                        isSuccess = false;
                        break;
                    }
                }
                else
                {//对象类型，判断是否其子类或本类
                    if (clazz != param.getClass() && !clazz.isAssignableFrom(param.getClass()))
                    {
                        isSuccess = false;
                        break;
                    }
                }
            }
            
            if (!isSuccess)
                continue;
            
            constructor = c;
            break;
        }
        
        if (constructor == null)
            throw new ExpressionException("方法表达式{"+this+"}，格式不正确，没有找到对应的构造函数");
        
        //_Integer,_Double可能格式不同，要匹配
        Class<?>[] paramTypes = constructor.getParameterTypes();
        for (int i=0;i<paramTypes.length;i++)
        {
            Class<?> clazz = paramTypes[i];
            if (!Types.isNumber(clazz))
                continue;
            
            if (Types.isDecimal(clazz))
            {//小数
                Object param = paramArray[i];
                if (!Types.isDecimal(param))
                    throw new ExpressionException("方法表达式{"+this+"}，格式不正确，参数不匹配，要求小数");

                //需要对double/float进行匹配
                if (Types.isDouble(clazz))
                    paramArray[i] = ((Number)param).doubleValue();
                else
                    paramArray[i] = ((Double)param).floatValue();
            }
            else 
            {//整数
                Object param = paramArray[i];
                if (!Types.isInteger(param))
                    throw new ExpressionException("方法表达式{"+this+"}，格式不正确，参数不匹配，要求整数");
                
                //需要对long/byte/short/int进行匹配
                if (Types.isLong(clazz))
                    paramArray[i] = ((Number)param).longValue();
                else if (Types.isInt(clazz))
                    paramArray[i] = ((Number)param).intValue();
                else if (Types.isShort(clazz))
                    paramArray[i] = ((Number)param).shortValue();
                else
                    paramArray[i] = ((Number)param).byteValue();
            }
        }
        
        //调用方法
        try
        {
            constructor.setAccessible(true);
            return constructor.newInstance(paramArray);
        }
        catch (Exception e)
        {
            if (e instanceof InvocationTargetException)
                throw new ExpressionException("方法表达式{"+this+"}，调用失败{"+((InvocationTargetException)e).getTargetException().getMessage()+"}");
            else
                throw new ExpressionException("方法表达式{"+this+"}，调用失败{"+e.getMessage()+"}");
        }
    }
    
    /** 对象方法 */
    private Object buildMethod(ZmlVariable variableMap) throws ExpressionException
    {
        Object target = obj.build(variableMap);
        if (target != null)
            return buildMethod(variableMap, target.getClass(), target);
        
        if (!(obj instanceof _Variable))
            return null;
        
        Class<?> targetClass = ((_Variable)obj).buildClass();
        if (targetClass == null)
            return null;
        
        return buildMethod(variableMap, targetClass, null);
    }
    
    /** 对象方法 */
    private Object buildMethod(ZmlVariable variableMap, Class<?> targetClass, Object target) throws ExpressionException
    {
        //1.先漏选方法，把名称不同的和要求静态方法但不是静态方法的去掉
        List<Method> normalMethodList = new ArrayList<>();
        List<Method> arrayMethodList = new ArrayList<>();
        
        Method[] methods = targetClass.getMethods();
        for (Method m : methods)
        {
            if (!m.getName().equals(methodName))
                continue;
            
            if (target == null && !Classes.isStaticMethod(m))
                continue;
            
            if (Classes.isMethodLastParamArray(m))
                arrayMethodList.add(m);
            else
                normalMethodList.add(m);
        }
        
        if (normalMethodList.isEmpty())
        {//未找到对应的方法
            throw new ExpressionException("方法表达式{"+this+"}，格式不正确，没有找到对应的方法名或要求是静态方法");
        }
        
        Object[] paramArray = new Object[paramList.size()];
        for (int i=0;i<paramList.size();i++)
        {
            paramArray[i] = paramList.get(i).build(variableMap);
        }
        
        //2.通过参数匹配，找到对应的方法
        Method method = null;
        
        //2.1优先找最后一个参数不是数组的方法
        for (Method m : normalMethodList)
        {
            Class<?>[] paramTypes = m.getParameterTypes();
            if (Classes.isMethodParamMatch(paramTypes, paramArray))
            {//正常的参数个数匹配成功
                method = m;
                break;
            }
        }
        
        if (method == null)
        {//2.2再找最后一个参数是数组的，转化个数相等再比较
            for (Method m : arrayMethodList)
            {
                //判断参数个数
                Class<?>[] paramTypes = m.getParameterTypes();
                int typeLen = paramTypes.length;
                int arrayLen = paramArray.length;
                if (typeLen > arrayLen)
                {//大于的
                    continue;
                }
                else if (typeLen == arrayLen)
                {//相等的
                    if (Classes.isMethodParamMatch(paramTypes, paramArray))
                    {
                        method = m;
                        break;
                    }
                }
                else
                {//小于的，则支持参数最后部分转化为多个参数方式String... params的形参，而实参是"abc", "bde"的情况
                    Class<?> paramLastType = paramTypes[typeLen-1].getComponentType();
                    Class<?>[] newParamTypes = new Class<?>[arrayLen];
                    for (int i=0;i<typeLen-1;i++)
                    {
                        newParamTypes[i] = paramTypes[i];
                    }
                    for (int i=typeLen-1;i<arrayLen;i++)
                    {
                        newParamTypes[i] = paramLastType;
                    }
                    
                    if (Classes.isMethodParamMatch(newParamTypes, paramArray))
                    {
                        method = m;
                        break;
                    }
                }
            }
        }
        
        if (method == null)
        {//2.3 方法参数没找到
            throw new ExpressionException("方法表达式{"+this+"}，格式不正确，方法名找到，但参数个数未对应");
        }
        
        //3._Integer,_Double可能格式不同，要匹配
        Class<?>[] paramTypes = method.getParameterTypes();
        for (int i=0;i<paramTypes.length;i++)
        {
            Class<?> clazz = paramTypes[i];
            if (!Types.isNumber(clazz))
                continue;
            
            if (Types.isDecimal(clazz))
            {//小数
                Object param = paramArray[i];
                if (!Types.isDecimal(param))
                    throw new ExpressionException("方法表达式{"+this+"}，格式不正确，方法名找到且参数个数一致，参数值不匹配，要求小数");

                //需要对double/float进行匹配
                if (Types.isDouble(clazz))
                    paramArray[i] = ((Number)param).doubleValue();
                else
                    paramArray[i] = ((Double)param).floatValue();
            }
            else 
            {//整数
                Object param = paramArray[i];
                if (!Types.isInteger(param))
                    throw new ExpressionException("方法表达式{"+this+"}，格式不正确，方法名找到且参数个数一致，参数不匹配，要求整数");
                
                //需要对long/byte/short/int进行匹配
                if (Types.isLong(clazz))
                    paramArray[i] = ((Number)param).longValue();
                else if (Types.isInt(clazz))
                    paramArray[i] = ((Number)param).intValue();
                else if (Types.isShort(clazz))
                    paramArray[i] = ((Number)param).shortValue();
                else
                    paramArray[i] = ((Number)param).byteValue();
            }
        }
        
        //4.最后一个形参是数组，而实参不是数组的转变为数组
        boolean lastParamIsArray = paramTypes.length > 0 && Types.isArray(paramTypes[paramTypes.length-1]);
        if (lastParamIsArray && !Types.isArray(paramArray[paramTypes.length-1]))
        {
            Class<?> paramLastType = paramTypes[paramTypes.length-1].getComponentType();
            int num = paramArray.length-paramTypes.length+1;
            Object[] objs = Classes.newInstance(paramLastType, num);
            for (int i=0;i<num;i++)
            {
                objs[i] = paramArray[paramTypes.length-1+i];
            }
            
            Object[] newParamArray = new Object[paramTypes.length];
            for (int i=0;i<newParamArray.length-1;i++)
            {
                newParamArray[i] = paramArray[i];
            }
            newParamArray[newParamArray.length-1] = objs;
            paramArray = newParamArray;
        }
        
        //5.调用方法
        try
        {
            method.setAccessible(true);
            if (Classes.isStaticMethod(method))
                return method.invoke(null, paramArray);
            else
                return method.invoke(target, paramArray);
        }
        catch (Exception e)
        {
            if (e instanceof InvocationTargetException)
                throw new ExpressionException("方法表达式{"+this+"}，调用失败{"+((InvocationTargetException)e).getTargetException().getMessage()+"}");
            else
                throw new ExpressionException("方法表达式{"+this+"}，调用失败{"+e.getMessage()+"}");
        }
    }
    
    @Override
    public String toString()
    {
        StringBuilder strb = new StringBuilder();
        if (obj != null)
        {
            strb.append(obj);
            if (obj instanceof _New)
                strb.append(" ");
            else
                strb.append(".");
        }
        
        strb.append(methodName).append("(");
        if (!paramList.isEmpty())
        {
            strb.append(paramList.get(0));
            for (int i=1;i<paramList.size();i++)
            {
                strb.append(", ").append(paramList.get(i));
            }
        }
        strb.append(")");
        
        return strb.toString();
    }
    
    public String getMethodName()
    {
        return methodName;
    }
    
    public Expression getObject()
    {
        return obj;
    }
    
    /** 合并前面的对象表达式 */
    public void setObject(Expression obj)
    {
        this.obj = obj;
    }
    
    /** 设置为New */
    public void setNew(Expression obj, String className)
    {
        this.obj = obj;
        this.methodName = className;
    }
    
    /** 解析括号得到实参列表 */
    public void parseBracketParamList(_Bracket bracket) throws ExpressionException
    {
        ArrayList<Expression> eList = bracket.getExpressionList();
        eList.trimToSize();
        
        //左右括号去除
        eList.remove(0);
        eList.remove(eList.size()-1);
        
        //解析所有操作符
        ExpressionParser.parse_Operator(eList);
        
        //对结果进行分析，除逗号外都是参数
        for (Expression expression : eList)
        {
            int type = expression.getType();
            if (type == COMMA)
                continue;//,除外
            
            paramList.add(expression);
        }
        
        eList = null;
        paramList.trimToSize();
    }
    
}
